#pragma once

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//守护进程（Daemon）
// 守护进程的特点：
// 脱离终端：不和任何终端关联（不会受用户注销影响）。
// 后台运行：没有交互界面，默默执行任务。
// 独立会话：不受父进程或终端信号干扰。
// 通常以 root 权限运行：可以管理系统资源（如 Web 服务器、数据库服务）。

//void (*signal(int signum, void (*handler)(int)))(int);
// signum：要处理的信号编号。
// handler：信号处理函数。
//SIG_IGN 表示忽略该信号。

const std::string nullfile = "/dev/null";
// /dev/null 是一个特殊的设备文件，它的行为：
// 读取时：立即返回 EOF（文件结束）。
// 写入时：直接丢弃数据（像黑洞一样）。

//Daemon()一运行，那么就开启了守护进程

void Daemon(const std::string &cwd = "")    //cwd不传入参数，则表示不更改当前调用进程的工作目录
{
    // 1. 忽略其他异常信号
    signal(SIGCLD, SIG_IGN); //SIGCLD（或 SIGCHLD）是 子进程状态变更信号（例如子进程退出时触发）,代码作用：防止子进程退出时变成僵尸进程（Zombie）
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);  //这些都是适合忽略的信号,防止这些信号导致进程退出
    //终止守护进程，可以通过kill命令

    // 2. 将自己变成独立的会话
    if (fork() > 0)
        exit(0);
    setsid();
    // setsid() 创建新会话
    // setsid() 会让当前进程：
    // 成为新会话的领头进程（Session Leader）。
    // 脱离原终端的控制（不再受 Ctrl+C、终端关闭等影响）。
    // 不再有控制终端（TTY），完全独立运行。

    // 3. 更改当前调用进程的工作目录
    if (!cwd.empty())
        chdir(cwd.c_str());   //int chdir(const char *path) : 将当前进程的工作目录更改为 path。

    // 4. 标准输入，标准输出，标准错误重定向至/dev/null
    int fd = open(nullfile.c_str(), O_RDWR);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
    // 这段代码的作用是 将进程的标准输入（stdin）、标准输出（stdout）和标准错误（stderr）重定向到 /dev/null，
    // 确保守护进程不会从终端读取输入或向终端输出任何内容。
    // 守护进程的需求：
    // 作为后台服务，它不应依赖终端输入（如键盘）。
    // 它的输出（如日志）应写入文件或系统日志（如 syslog），而非终端。
    // 避免对应终端设备的关闭导致进程阻塞或崩溃。
}